﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MySQLQueryMonitor.Util;
using System.Collections.ObjectModel;
using PcapDotNet.Core;
using System.Windows;
using System.Windows.Threading;
using System.Threading.Tasks;
using MySQLQueryMonitor.Model;

namespace MySQLQueryMonitor
{
    public class MainModel : NotificationObject
    {
        public MainModel()
        {
            commandLog = new ObservableCollection<MySQLCommandModel>();

            Port = 3306;
        }

        #region private
        private CancellationTokenSource tokenSource;

        private bool isMonitoring;
        private ObservableCollection<MySQLCommandModel> commandLog;
        private LivePacketDevice device;

        #endregion

        #region properties
        public bool IsDeviceSelected
        {
            get { return Device != null; }
        }
        public bool CanChangeDevice
        {
            get { return !IsMonitoring; }
        }
        public bool IsMonitoring
        {
            get { return isMonitoring; }
            set
            {
                if (isMonitoring == false)
                {
                    isMonitoring = StartMonitoring();
                }
                else
                {
                    isMonitoring = !StopMonitoring();
                }
                RaisePropertyChanged(() => IsMonitoring);
                RaisePropertyChanged(() => CanChangeDevice);
            }
        }

        private ushort port;

        public ushort Port
        {
            get { return port; }
            set { port = value; RaisePropertyChanged(() => Port); }
        }

        public ObservableCollection<MySQLCommandModel> CommandLog
        {
            get { return commandLog; }
        }

        public LivePacketDevice Device
        {
            get { return device; }
            set {
                device = value;
                RaisePropertyChanged(() => Device);
                RaisePropertyChanged(() => IsDeviceSelected);
            }
        }
        #endregion

        private bool StartMonitoring()
        {
            try
            {
                var monitor = new MySQLQueryMonitorModule.MySQLQueryMonitor(device, Port, 1000);

                tokenSource = new CancellationTokenSource();
                var token = tokenSource.Token;
                var dispatcher = Dispatcher.CurrentDispatcher;
                Task.Factory.StartNew(() =>
                {
                    while (token.IsCancellationRequested == false)
                    {
                        HandlePacket(dispatcher, monitor);
                    }
                    monitor.Dispose();
                }, token);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, null, MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }

            return true;
        }
        private bool StopMonitoring()
        {
            try
            {
                tokenSource.Cancel();
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        private void HandlePacket(Dispatcher dispatcher, MySQLQueryMonitorModule.MySQLQueryMonitor monitor)
        {
            var packet = monitor.Receive();
            if (packet == null)
            {
                return;
            }

            var tcp = packet.Ethernet.IpV4.Tcp;
            if (tcp.DestinationPort != Port)
            {
                return;
            }

            try
            {
                var command = MySQLQueryMonitorModule.parseMySQLCommandPacket(packet);
                var query = command as MySQLQueryMonitorModule.MySQLCommand.COM_QUERY;
                if (query == null)
                {
                    return;
                }

                dispatcher.Invoke((Action)(() => CommandLog.Add(new MySQLCommandModel(packet.Timestamp, query.Item))));
            }
            catch (Exception)
            {
            }
        }
    }
}
